﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Text
Imports System.Configuration
Imports System.Configuration.Provider
Imports System.Windows.Forms
Imports System.Collections.Specialized
Imports Microsoft.Win32
Imports System.Xml
Imports System.Runtime.Serialization
Imports System.Xml.Serialization

Namespace My

    'This class allows you to handle specific events on the settings class:
    ' The SettingChanging event is raised before a setting's value is changed.
    ' The PropertyChanged event is raised after a setting's value is changed.
    ' The SettingsLoaded event is raised after the setting values are loaded.
    ' The SettingsSaving event is raised before the setting values are saved.
    Partial Friend NotInheritable Class MySettings
        Public Sub SavePortable(ByVal Pth As String)
            Me.Save()
            Dim pv As New PortableSettingsProvider(Pth)
            Providers.Add(pv)
            Dim props As New System.Configuration.SettingsPropertyCollection
            Dim dpv As New Dictionary(Of SettingsProperty, SettingsProvider)


            For Each prop As SettingsProperty In Properties
                Dim at As Attribute = (From a As DictionaryEntry In prop.Attributes Where TypeOf a.Value Is System.Configuration.UserScopedSettingAttribute Select a.Value).FirstOrDefault
                If at IsNot Nothing Then
                    props.Add(prop)
                    dpv.Add(prop, prop.Provider)

                End If
            Next


            Dim pvals As System.Configuration.SettingsPropertyValueCollection = (From p In dpv.Values.Distinct Where TypeOf p Is LocalFileSettingsProvider Select p.GetPropertyValues(Me.Context, props)).FirstOrDefault

            For Each prop As SettingsProperty In props
                prop.Provider = pv

            Next
            pv.SetPropertyValues(Me.Context, pvals)

            Me.Save()
            For Each prop As SettingsProperty In props
                prop.Provider = dpv.Item(prop)


            Next
            Providers.Remove(pv.Name)

        End Sub

        Public Sub New()
            MyBase.New()
            If Me.InstallType = InstallTypes.Portable.ToString Then
                Dim pv As New PortableSettingsProvider
                Providers.Add(pv)
                For Each prop As SettingsProperty In Properties

                    Dim at As Attribute = (From a As DictionaryEntry In prop.Attributes Where TypeOf a.Value Is System.Configuration.UserScopedSettingAttribute Select a.Value).FirstOrDefault
                    If at IsNot Nothing Then prop.Provider = pv

                Next

            End If

        End Sub

        Private Sub MySettings_SettingsLoaded(sender As Object, e As System.Configuration.SettingsLoadedEventArgs) Handles Me.SettingsLoaded


        End Sub


        Private Sub MySettings_SettingsSaving(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles Me.SettingsSaving

        End Sub
    End Class
End Namespace

Public Class PortableSettingsProvider
    Inherits SettingsProvider

    Const SETTINGSROOT As String = "Settings" 'XML Root Node

    Public Overrides Sub Initialize(ByVal name As String, ByVal col As NameValueCollection)
        MyBase.Initialize(Me.ApplicationName, col)
    End Sub

    Public Overrides Property ApplicationName() As String
        Get
            If Application.ProductName.Trim.Length > 0 Then
                Return Application.ProductName
            Else
                Dim fi As New IO.FileInfo(Application.ExecutablePath)
                Return fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length)
            End If
        End Get
        Set(ByVal value As String)
            'Do nothing
        End Set
    End Property

    Overridable Function GetAppSettingsPath() As String
        'Used to determine where to store the settings
        If String.IsNullOrEmpty(FilePath) Then
            Dim fi As New System.IO.FileInfo(Application.ExecutablePath)
            Return fi.DirectoryName
        Else
            Return FilePath
        End If

    End Function

    Overridable Function GetAppSettingsFilename() As String
        'Used to determine the filename to store the settings
        Return ApplicationName & ".settings"
    End Function

    Public Overrides Sub SetPropertyValues(ByVal context As SettingsContext, ByVal propvals As SettingsPropertyValueCollection)
        'Iterate through the settings to be stored
        'Only dirty settings are included in propvals, and only ones relevant to this provider
        For Each propval As SettingsPropertyValue In propvals
            SetValue(propval)
        Next

        Try
            SettingsXML.Save(IO.Path.Combine(GetAppSettingsPath, GetAppSettingsFilename))
        Catch ex As Exception
            'Ignore if cant save, device been ejected
        End Try
    End Sub

    Public Overrides Function GetPropertyValues(ByVal context As SettingsContext, ByVal props As SettingsPropertyCollection) As SettingsPropertyValueCollection
        'Create new collection of values
        Dim values As SettingsPropertyValueCollection = New SettingsPropertyValueCollection()

        'Iterate through the settings to be retrieved
        For Each setting As SettingsProperty In props

            Dim value As SettingsPropertyValue = New SettingsPropertyValue(setting)
            value.IsDirty = False
            value.SerializedValue = GetValue(setting)
            values.Add(value)
        Next
        Return values
    End Function

    Private m_SettingsXML As Xml.XmlDocument = Nothing

    Private ReadOnly Property SettingsXML() As Xml.XmlDocument
        Get
            'If we dont hold an xml document, try opening one.  
            'If it doesnt exist then create a new one ready.
            If m_SettingsXML Is Nothing Then
                m_SettingsXML = New Xml.XmlDocument

                Try
                    m_SettingsXML.Load(IO.Path.Combine(GetAppSettingsPath, GetAppSettingsFilename))
                Catch ex As Exception
                    'Create new document
                    Dim dec As XmlDeclaration = m_SettingsXML.CreateXmlDeclaration("1.0", "utf-8", String.Empty)
                    m_SettingsXML.AppendChild(dec)

                    Dim nodeRoot As XmlNode

                    nodeRoot = m_SettingsXML.CreateNode(XmlNodeType.Element, SETTINGSROOT, "")
                    m_SettingsXML.AppendChild(nodeRoot)
                End Try
            End If

            Return m_SettingsXML
        End Get
    End Property

    Private Function GetValue(ByVal setting As SettingsProperty) As String
        Dim ret As String = ""

        Try
            If IsRoaming(setting) Then
                ret = SettingsXML.SelectSingleNode(SETTINGSROOT & "/" & setting.Name).InnerText
            Else
                ret = SettingsXML.SelectSingleNode(SETTINGSROOT & "/" & My.Computer.Name & "/" & setting.Name).InnerText
            End If

        Catch ex As Exception
            If Not setting.DefaultValue Is Nothing Then
                ret = setting.DefaultValue.ToString
            Else
                ret = ""
            End If
        End Try

        Return ret
    End Function

    Private Sub SetValue(ByVal propVal As SettingsPropertyValue)

        Dim MachineNode As Xml.XmlElement
        Dim SettingNode As Xml.XmlElement

        'Determine if the setting is roaming.
        'If roaming then the value is stored as an element under the root
        'Otherwise it is stored under a machine name node 
        Try
            If IsRoaming(propVal.Property) Then
                SettingNode = DirectCast(SettingsXML.SelectSingleNode(SETTINGSROOT & "/" & propVal.Name), XmlElement)
            Else
                SettingNode = DirectCast(SettingsXML.SelectSingleNode(SETTINGSROOT & "/" & My.Computer.Name & "/" & propVal.Name), XmlElement)
            End If
        Catch ex As Exception
            SettingNode = Nothing
        End Try

        'Check to see if the node exists, if so then set its new value
        If Not SettingNode Is Nothing Then
            SettingNode.InnerText = propVal.SerializedValue.ToString
        Else
            If IsRoaming(propVal.Property) Then
                'Store the value as an element of the Settings Root Node
                SettingNode = SettingsXML.CreateElement(propVal.Name)
                SettingNode.InnerText = propVal.SerializedValue.ToString
                SettingsXML.SelectSingleNode(SETTINGSROOT).AppendChild(SettingNode)
            Else
                'Its machine specific, store as an element of the machine name node,
                'creating a new machine name node if one doesnt exist.
                Try
                    MachineNode = DirectCast(SettingsXML.SelectSingleNode(SETTINGSROOT & "/" & My.Computer.Name), XmlElement)
                Catch ex As Exception
                    MachineNode = SettingsXML.CreateElement(My.Computer.Name)
                    SettingsXML.SelectSingleNode(SETTINGSROOT).AppendChild(MachineNode)
                End Try

                If MachineNode Is Nothing Then
                    MachineNode = SettingsXML.CreateElement(My.Computer.Name)
                    SettingsXML.SelectSingleNode(SETTINGSROOT).AppendChild(MachineNode)
                End If

                SettingNode = SettingsXML.CreateElement(propVal.Name)
                SettingNode.InnerText = propVal.SerializedValue.ToString
                MachineNode.AppendChild(SettingNode)
            End If
        End If
    End Sub

    Private Function IsRoaming(ByVal prop As SettingsProperty) As Boolean
        'Determine if the setting is marked as Roaming
        For Each d As DictionaryEntry In prop.Attributes
            Dim a As Attribute = DirectCast(d.Value, Attribute)
            If TypeOf a Is System.Configuration.SettingsManageabilityAttribute Then
                Return True
            End If
        Next
        Return False
    End Function
    Private FilePath As String
    Public Sub New(ByVal pth As String)
        MyClass.New()
        FilePath = pth
    End Sub
    Public Sub New()
        MyBase.New()

    End Sub
    Public Overrides ReadOnly Property Name As String
        Get
            Return "PortableSettingsProvider"
        End Get
    End Property
End Class


Enum InstallTypes
    MSI
    Portable
End Enum


Public Class ScheduleColorSetting
    Inherits SerializableDictionary(Of String, XmlColor)
    'Implements IXmlSerializable


    Public Overrides Sub GetObjectData(info As System.Runtime.Serialization.SerializationInfo, context As System.Runtime.Serialization.StreamingContext)
        MyBase.GetObjectData(info, context)
        If info Is Nothing Then
            Throw New System.ArgumentNullException("info")
        End If
        Dim ds = Me.ToStringStringDictionary
        info.AddValue("Colors", ds, ds.GetType)

    End Sub
    Private Function ToStringStringDictionary() As Dictionary(Of String, String)
        Dim ds As New Dictionary(Of String, String)

        For Each k In Me.Keys
            If Me(k) = Color.Empty Then
                ds.Add(k, "Empty")
            Else
                ds.Add(k, ColorTranslator.ToHtml(Me(k)))
            End If


        Next
        Return ds
    End Function
    Public Sub New()
        MyBase.New()
    End Sub
    Protected Sub New(ByVal info As SerializationInfo, _
    ByVal context As StreamingContext)
        MyBase.New()

        If info Is Nothing Then
            Throw New System.ArgumentNullException("info")
        End If
        Dim ds = info.GetValue("Colors", GetType(Dictionary(Of String, String)))

        Me.Clear()
        For Each k In ds.keys
            Me.Add(k, ColorTranslator.FromHtml(ds(k)))
        Next

    End Sub


    'Public Function GetSchema() As System.Xml.Schema.XmlSchema Implements System.Xml.Serialization.IXmlSerializable.GetSchema
    '    Return Nothing
    'End Function

    'Public Sub ReadXml(reader As System.Xml.XmlReader) Implements System.Xml.Serialization.IXmlSerializable.ReadXml

    '    If reader.Read() Then
    '        Dim strtmp As String = reader.ReadContentAsString
    '        Dim valSp = strtmp.Split(";")
    '        Me.Clear()

    '        For Each v In valSp
    '            If Not String.IsNullOrWhiteSpace(v) Then

    '                Dim v2 = v.Split(",")
    '                If v2(1) <> "Empty" Then
    '                    Me.Add(v2(0), ColorTranslator.FromHtml(v2(1)))
    '                Else
    '                    Me.Add(v2(0), Color.Empty)

    '                End If
    '            End If

    '        Next
    '    End If
    '    reader.ReadEndElement()
    'End Sub

    'Public Sub WriteXml(writer As System.Xml.XmlWriter) Implements System.Xml.Serialization.IXmlSerializable.WriteXml


    '    Dim ds = Me.ToStringStringDictionary
    '    Dim sb As New System.Text.StringBuilder
    '    For Each k In ds.Keys
    '        sb.AppendFormat("{0},{1};", k, ds(k))
    '    Next
    '    writer.WriteString(sb.ToString.TrimEnd(","))


    'End Sub
End Class

Public Class XmlColor
    Private color_ As Color = Color.Black

    Public Sub New()
    End Sub
    Public Sub New(c As Color)
        color_ = c
    End Sub


    Public Function ToColor() As Color
        Return color_
    End Function

    Public Sub FromColor(c As Color)
        color_ = c
    End Sub

    Public Shared Widening Operator CType(x As XmlColor) As Color
        Return x.ToColor()
    End Operator

    Public Shared Widening Operator CType(c As Color) As XmlColor
        Return New XmlColor(c)
    End Operator

    <XmlAttribute()> _
    Public Property Web() As String
        Get
            Return ColorTranslator.ToHtml(color_)
        End Get
        Set(value As String)
            Try
                If Alpha = &HFF Then
                    ' preserve named color value if possible
                    color_ = ColorTranslator.FromHtml(value)
                Else
                    color_ = Color.FromArgb(Alpha, ColorTranslator.FromHtml(value))
                End If
            Catch generatedExceptionName As Exception
                color_ = Color.Black
            End Try
        End Set
    End Property

    <XmlAttribute()> _
    Public Property Alpha() As Byte
        Get
            Return color_.A
        End Get
        Set(value As Byte)
            If value <> color_.A Then
                ' avoid hammering named color if no alpha change
                color_ = Color.FromArgb(value, color_)
            End If
        End Set
    End Property

    Public Function ShouldSerializeAlpha() As Boolean
        Return Alpha < &HFF
    End Function
End Class